package cn.iocoder.yudao.module.study.formatResult.aspect;

import cn.hutool.core.date.LocalDateTimeUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.study.formatResult.annotation.FormatField;
import cn.iocoder.yudao.module.study.formatResult.annotation.FormatResult;
import cn.iocoder.yudao.module.study.formatResult.annotation.IgnoreFormatResult;
import cn.iocoder.yudao.module.study.formatResult.constant.FormatConstant;
import cn.iocoder.yudao.module.study.formatResult.enums.FormatTypeEnum;
import cn.iocoder.yudao.module.system.api.dict.DictDataApi;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.util.*;

/**
 * @Author cjc
 * @Date 2024/5/16 17:14
 * @Description: 结果格式切面类
 * @Version 1.0
 */
@Aspect
@Order(1)
@Component
@Slf4j
public class FormatResultAspect {

    /**
     * 字典值本地缓存-加快翻译速度
     * 1、先读取localCacheMap 2、读取redis 3、最后读取数据库
     */
    private Map<String, String> localCacheMap;

    @Resource
    private DictDataApi dictDataApi;

    @Pointcut(value = "@annotation(cn.iocoder.yudao.module.study.formatResult.annotation.FormatResult)")
    public void setFormat() {}


    @AfterReturning(value = "setFormat()",returning = "result")
    public void doAfterReturning(JoinPoint joinPoint, Object result) throws IllegalAccessException{
        LocalDateTimeUtil.isOverlap(LocalDateTime.now(),LocalDateTime.now(),LocalDateTime.now(),LocalDateTime.now());
        //如果方法上有IgnoreFormatResult注解，则忽略格式化。
        //添加这个注解的目的是 如果该切面通过 表达式 作用于所有controller时，需要这个注解进行忽略某个接口
        boolean ignoreFormat = isIgnoreFormat(joinPoint);
        if (ignoreFormat){
            return;
        }

        //每次进入方法先清空本地缓存
        if (this.localCacheMap == null) {
            this.localCacheMap = new HashMap();
        } else {
            this.localCacheMap.clear();
        }


        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        FormatResult annotation = signature.getMethod().getAnnotation(FormatResult.class);
        boolean isFormatExtra = annotation.formatAsExtra();
        if (isFormatExtra){
            //格式化为额外的字段
            formatAsExtra(result);
        }else{
            //覆盖原字段
            formatAsCover(result);
        }
    }



    /**
     * 1、格式化为一个新字段
     * 1.1 校验结果类型
     */
    private void formatAsExtra(Object result){
        this.localCacheMap = (Map)(this.localCacheMap == null ? new HashMap() : this.localCacheMap);

        if (result instanceof List){
            //1、如果输出结果类型为List
            //desc: 循环遍历这个集合，将每个对象转换为JSONObject，将JSONObject添加到ArrayList中，用新的ArrayList覆盖旧的结果
            ArrayList items = new ArrayList();
            boolean flag = false;
            
            Iterator var1 = ((List) result).iterator();
            while (var1.hasNext()){
                Object record = var1.next();
                if (record != null){
                    JSONObject item = this.parseJson(record);
                    items.add(item);
                    flag = true;
                }
            }

            if (flag){
                ((List) result).clear();
                ((List) result).addAll(items);
            }

        }else if (result instanceof CommonResult){
            //2、如果输出结果类型为CommonResult

            ArrayList items;
            if ( ((CommonResult) result).getData() instanceof List){
                //2.1 desc: 如果CommonResult里面的Data为List，循环遍历这个集合，将每个对象转换为JSONObject，将JSONObject添加到ArrayList中，用新的ArrayList覆盖旧的结果
                items = new ArrayList();
                boolean flag = false;

                Iterator var1 =  ((List)((CommonResult) result).getData()).iterator();
                while (var1.hasNext()){
                    Object record = var1.next();
                    if (record != null){
                        JSONObject item = this.parseJson(record);
                        items.add(item);
                        flag = true;
                    }
                }

                if (flag){
                    ((List)((CommonResult) result).getData()).clear();
                    ((List)((CommonResult) result).getData()).addAll(items);
                }

            }else if ( ((CommonResult)result).getData() instanceof PageResult){
                //2.2 desc: 如果CommonResult里面的Data为PageResult，获取PageResult里面的List，循环遍历这个集合，将每个对象转换为JSONObject，将JSONObject添加到ArrayList中，用新的ArrayList覆盖旧的结果
                if (((PageResult) ((CommonResult) result).getData()).getList() != null){
                    // CommonResult<PageResult<T>>
                    items = new ArrayList();
                    boolean flag = false;
                    
                    Iterator var1 =  (((PageResult) ((CommonResult) result).getData()).getList()).iterator();
                    while (var1.hasNext()){
                        Object record = var1.next();
                        if (record != null){
                            JSONObject item = this.parseJson(record);
                            items.add(item);
                            flag = true;
                        };
                    }

                    if (flag){
                        (((PageResult) ((CommonResult) result).getData()).getList()).clear();
                        (((PageResult) ((CommonResult) result).getData()).getList()).addAll(items);
                    }

                }
            }else if (((CommonResult) result).getData() instanceof String  || ((CommonResult) result).getData() instanceof Boolean
                    || ((CommonResult) result).getData() instanceof Long){
                //2.3 desc: 如果CommonResult里面的Data为String、Boolean、Long 类型不做处理，可补充其他需要忽略的类型...
                // ignored
            } else{
                //2.4 desc: 这里指的是CommonResult里面的Data是一个实体类，将这个对象转换为JSONObject，将新的JSONObject覆盖原来的对象
                Object record = ((CommonResult) result).getData();
                JSONObject item = this.parseJson(record);
                ((CommonResult) result).setData(item);
            }
        }
    }


    /**
     * 1、格式化为一个新字段
     * 1.2 Object 转 JSONObject
     */
    private JSONObject parseJson(Object record){
        ObjectMapper mapper = new ObjectMapper();
        String json = "{}";

        try {
            json = mapper.writeValueAsString(record);
        } catch (JsonProcessingException var13) {
            log.error("json解析失败" + var13.getMessage(), var13);
        }

        JSONObject item = JSONObject.parseObject(json);
        this.translate(record,item);
        return item;
    }

    /**
     * 1、格式化为一个新字段
     * 1.3 字段翻译
     */
    private void translate(Object record,JSONObject item){
        Field[] var9 =getAllFields(record);
        int var10 = var9.length;
        for(int var11 = 0; var11 < var10; ++var11) {
            Field field = var9[var11];

            FormatField formatField;
            if ((formatField = field.getAnnotation(FormatField.class)) != null) {

                //field的值,字段的值
                String formatVal = String.valueOf(item.get(field.getName()));

                //注解的 code
                String code = formatField.formatCode();

                //注解的 格式化类型
                int type = formatField.type();

                //根据格式化类型选择格式化的房四海
                if (FormatTypeEnum.FIX.getType() == type){
                    //通过固定值的方式格式化
                    item.put(field.getName() + formatField.suffix(), FormatConstant.getValue(code,formatVal));
                }else if (FormatTypeEnum.DICT.getType() == type){
                    String mapKey = code + "_" + formatVal;
                    String tmpValue;
                    //先取本地缓存，再取redis
                    if (this.localCacheMap.containsKey(mapKey)) {
                        tmpValue = this.localCacheMap.get(mapKey);
                    } else {
                        //通过数据字典取值（先redis后mysql）
                        tmpValue = dictDataApi.getDictDataByCodeAndValue(code, formatVal);
                        this.localCacheMap.put(mapKey, tmpValue);
                    }
                    item.put(field.getName() + formatField.suffix(), tmpValue);
                }else{
                    //其他方式
                }
            }
        }
    }


    /**
     * 2、覆盖原字段
     * 2.1 校验结果类型
     */
    private void formatAsCover(Object result)  throws IllegalAccessException {
        this.localCacheMap = (Map)(this.localCacheMap == null ? new HashMap() : this.localCacheMap);

        if (result instanceof List){
            //1、如果输出结果类型为List
            //desc: 循环遍历这个集合，将每个对象转换为JSONObject，将JSONObject添加到ArrayList中，用新的ArrayList覆盖旧的结果

            Iterator var1 = ((List) result).iterator();
            while (var1.hasNext()){
                Object record = var1.next();
                if (record != null){
                    this.parse(record);
                }
            }

        }else if (result instanceof CommonResult){
            //2、如果输出结果类型为CommonResult

            if ( ((CommonResult) result).getData() instanceof List){
                //2.1 desc: 如果CommonResult里面的Data为List，循环遍历这个集合，将每个对象转换为JSONObject，将JSONObject添加到ArrayList中，用新的ArrayList覆盖旧的结果

                Iterator var1 =  ((List)((CommonResult) result).getData()).iterator();
                while (var1.hasNext()){
                    Object record = var1.next();
                    if (record != null){
                        this.parse(record);
                    }
                }

            }else if ( ((CommonResult)result).getData() instanceof PageResult){
                //2.2 desc: 如果CommonResult里面的Data为PageResult，获取PageResult里面的List，循环遍历这个集合，将每个对象转换为JSONObject，将JSONObject添加到ArrayList中，用新的ArrayList覆盖旧的结果
                if (((PageResult) ((CommonResult) result).getData()).getList() != null){
                    // CommonResult<PageResult<T>>

                    Iterator var1 =  (((PageResult) ((CommonResult) result).getData()).getList()).iterator();
                    while (var1.hasNext()){
                        Object record = var1.next();
                        if (record != null){
                            this.parse(record);
                        };
                    }

                }
            }else if (((CommonResult) result).getData() instanceof String  || ((CommonResult) result).getData() instanceof Boolean
                    || ((CommonResult) result).getData() instanceof Long){
                //2.3 desc: 如果CommonResult里面的Data为String、Boolean、Long 类型不做处理，可补充其他需要忽略的类型...
                // ignored
            } else{
                //2.4 desc: 这里指的是CommonResult里面的Data是一个实体类，将这个对象转换为JSONObject，将新的JSONObject覆盖原来的对象
                Object record = ((CommonResult) result).getData();
                if (record!=null){
                    this.parse(record);
                }
            }
        }

    }

    /**
     * 2、覆盖原字段
     * 2.2 翻译字段
     * @param record 实体
     * @throws IllegalAccessException 异常
     */
    private void parse(Object record) throws IllegalAccessException {
        Field[] fields = getAllFields(record);
        for (Field field : fields) {
            FormatField formatField;
            if ((formatField = field.getAnnotation(FormatField.class)) != null) {
                field.setAccessible(true);
                //field的值,字段的值

                Object s = field.get(record);
                String formatVal = String.valueOf(s);
                //注解的 code
                String code = formatField.formatCode();

                //注解的 格式化类型
                int type = formatField.type();

                //根据格式化类型选择格式化的房四海
                String tmpValue;
                if (FormatTypeEnum.FIX.getType() == type){
                    //通过固定值的方式格式化
                    tmpValue = FormatConstant.getValue(code, formatVal);
                }else if (FormatTypeEnum.DICT.getType() == type){
                    String mapKey = code + "_" + formatVal;
                    //先取本地缓存，再取redis
                    if (this.localCacheMap.containsKey(mapKey)) {
                        tmpValue = this.localCacheMap.get(mapKey);
                    } else {
                        //通过数据字典取值（先redis后mysql）
                        tmpValue = dictDataApi.getDictDataByCodeAndValue(code, formatVal);
                        this.localCacheMap.put(mapKey, tmpValue);
                    }
                }else{
                    //其他方式
                    tmpValue = "";
                }
                convertFieldValue(field,record,s,tmpValue);
            }

        }
    }

    /**
     * 2、覆盖原字段
     * 2.3 只有当待转换的值为String类型才进行转换
     * @param field 字段
     * @param record 实体
     * @param tempValue 原字段
     * @param realValue 翻译后的字段
     * @throws IllegalAccessException
     */
    private void convertFieldValue(Field field,Object record,Object tempValue,String realValue) throws IllegalAccessException {
        if (tempValue instanceof String){
            field.set(record,realValue);
        }else {
            //其他类型不作处理
        }
    }

    /**
     * 获取类的所有属性，包括父类
     *
     * @param object
     * @return
     */
//    public static Field[] getAllFields(Object object) {
//        Class<?> clazz = object.getClass();
//        List<Field> fieldList = new ArrayList<>();
//        while (clazz != null) {
//            fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
//            clazz = clazz.getSuperclass();
//        }
//        Field[] fields = new Field[fieldList.size()];
//        fieldList.toArray(fields);
//        return fields;
//    }
    /**
     * 是否忽略某个接口不进行格式化
     * @param joinPoint joinPoint
     * @return true or false
     */
    private boolean isIgnoreFormat(JoinPoint joinPoint){
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        IgnoreFormatResult annotation = signature.getMethod().getAnnotation(IgnoreFormatResult.class);
        return annotation != null;
    }

    private static Field[] getAllFields(Object object) {
        Class<?> clazz = object.getClass();

        ArrayList fieldList;

        for (fieldList = new ArrayList(); clazz != null; clazz = clazz.getSuperclass()){
            fieldList.addAll(new ArrayList(Arrays.asList(clazz.getDeclaredFields())));
        }
        Field[] fields = new Field[fieldList.size()];
        fieldList.toArray(fields);
        return fields;
    }
}
